/*******************************************************************************
 *
 * Copyright (c) 2000-2003 Intel Corporation
 * All rights reserved.
 * Copyright (c) 2012 France Telecom All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * - Neither name of Intel Corporation nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************/

/*!
 * \file
 */

#include "ixmlparser.h"

#include <assert.h>
#include <stdlib.h> /* for free(), malloc() */
#include <string.h>

void ixmlNode_init(IXML_Node *nodeptr)
{
        assert(nodeptr != NULL);

        memset(nodeptr, 0, sizeof(IXML_Node));
}

void ixmlCDATASection_init(IXML_CDATASection *nodeptr)
{
        memset(nodeptr, 0, sizeof(IXML_CDATASection));
}

void ixmlCDATASection_free(IXML_CDATASection *nodeptr)
{
        if (nodeptr != NULL) {
                ixmlNode_free((IXML_Node *)nodeptr);
        }
}

/*!
 * \brief Frees a node content.
 */
static void ixmlNode_freeSingleNode(
        /*! [in] The node to free. */
        IXML_Node *nodeptr)
{
        IXML_Element *element = NULL;

        if (nodeptr != NULL) {
                if (nodeptr->nodeName != NULL) {
                        free(nodeptr->nodeName);
                }
                if (nodeptr->nodeValue != NULL) {
                        free(nodeptr->nodeValue);
                }
                if (nodeptr->namespaceURI != NULL) {
                        free(nodeptr->namespaceURI);
                }
                if (nodeptr->prefix != NULL) {
                        free(nodeptr->prefix);
                }
                if (nodeptr->localName != NULL) {
                        free(nodeptr->localName);
                }
                switch (nodeptr->nodeType) {
                case eELEMENT_NODE:
                        element = (IXML_Element *)nodeptr;
                        free(element->tagName);
                        break;
                default:
                        break;
                }
                free(nodeptr);
        }
}

#if 0
/*
 * Old implementation of ixmlNode_free(). Due to its recursive nature, it was
 * succeptible to attacks overflowing the stack.
 *
 * void ixmlNode_free(IXML_Node *nodeptr)
 */
void ixmlNode_recursive_free(IXML_Node *nodeptr)
{
        if (nodeptr != NULL) {
#ifdef IXML_HAVE_SCRIPTSUPPORT
                IXML_BeforeFreeNode_t hndlr = Parser_getBeforeFree();
                if (hndlr != NULL)
                        hndlr(nodeptr);
#endif
                ixmlNode_free(nodeptr->firstChild);
                ixmlNode_free(nodeptr->nextSibling);
                ixmlNode_free(nodeptr->firstAttr);
                ixmlNode_freeSingleNode(nodeptr);
        }
}
#endif

/*
 *  void ixmlNode_non_recursive_free(IXML_Node *nodeptr)
 */
void ixmlNode_free(IXML_Node *nodeptr)
{
        IXML_Node *curr_child;
        IXML_Node *prev_child;
        IXML_Node *next_child;
        IXML_Node *curr_attr;
        IXML_Node *next_attr;

        if (nodeptr) {
#ifdef IXML_HAVE_SCRIPTSUPPORT
                IXML_BeforeFreeNode_t hndlr = Parser_getBeforeFree();
#endif
                prev_child = nodeptr;
                next_child = nodeptr->firstChild;
                do {
                        curr_child = next_child;
                        do {
                                while (curr_child) {
                                        prev_child = curr_child;
                                        curr_child = curr_child->firstChild;
                                }
                                curr_child = prev_child;
                                while (curr_child) {
                                        prev_child = curr_child;
                                        curr_child = curr_child->nextSibling;
                                }
                                curr_child = prev_child;
                                next_child = curr_child->firstChild;
                        } while (next_child);
                        curr_child = prev_child;
                        /* current is now the last sibling of the last child. */
                        /* Delete the attribute nodes of this child */
                        /* Attribute nodes only have siblings. */
                        curr_attr = curr_child->firstAttr;
                        while (curr_attr) {
                                next_attr = curr_attr->nextSibling;
                                ixmlNode_freeSingleNode(curr_attr);
                                curr_attr = next_attr;
                        }
                        curr_child->firstAttr = 0;
                        /* Return */
                        if (curr_child != nodeptr) {
                                if (curr_child->prevSibling) {
                                        next_child = curr_child->prevSibling;
                                        next_child->nextSibling = 0;
                                } else {
                                        next_child = curr_child->parentNode;
                                        next_child->firstChild = 0;
                                }
                        }
#ifdef IXML_HAVE_SCRIPTSUPPORT
                        if (hndlr) {
                                hndlr(curr_child);
                        }
#endif
                        ixmlNode_freeSingleNode(curr_child);
                } while (curr_child != nodeptr);
        }
}

const DOMString ixmlNode_getNodeName(IXML_Node *nodeptr)
{
        if (nodeptr != NULL) {
                return nodeptr->nodeName;
        }

        return NULL;
}

const DOMString ixmlNode_getLocalName(IXML_Node *nodeptr)
{
        if (nodeptr != NULL) {
                return nodeptr->localName;
        }

        return NULL;
}

/*!
 * \brief Sets the namespace URI of the node.
 */
static int ixmlNode_setNamespaceURI(
        /*! [in] The \b Node on which to operate. */
        IXML_Node *nodeptr,
        /*! [in] The name space string to set. */
        const char *namespaceURI)
{
        if (nodeptr == NULL) {
                return IXML_INVALID_PARAMETER;
        }

        if (nodeptr->namespaceURI != NULL) {
                free(nodeptr->namespaceURI);
                nodeptr->namespaceURI = NULL;
        }

        if (namespaceURI != NULL) {
                nodeptr->namespaceURI = strdup(namespaceURI);
                if (nodeptr->namespaceURI == NULL) {
                        return IXML_INSUFFICIENT_MEMORY;
                }
        }

        return IXML_SUCCESS;
}

/*
 * \brief Set the prefix of the node.
 */
static int ixmlNode_setPrefix(
        /*! [in] The \b Node on which to operate. */
        IXML_Node *nodeptr,
        /*! [in] The prefix string to set. */
        const char *prefix)
{
        if (nodeptr == NULL) {
                return IXML_INVALID_PARAMETER;
        }

        if (nodeptr->prefix != NULL) {
                free(nodeptr->prefix);
                nodeptr->prefix = NULL;
        }

        if (prefix != NULL) {
                nodeptr->prefix = strdup(prefix);
                if (nodeptr->prefix == NULL) {
                        return IXML_INSUFFICIENT_MEMORY;
                }
        }

        return IXML_SUCCESS;
}

/*!
 * \brief Set the localName of the node.
 *
 * \return IXML_SUCCESS or failure.
 */
static int ixmlNode_setLocalName(
        /*! [in] The pointer to the node. */
        IXML_Node *nodeptr,
        /*! [in] The local name to set. */
        const char *localName)
{
        assert(nodeptr != NULL);

        if (nodeptr->localName != NULL) {
                free(nodeptr->localName);
                nodeptr->localName = NULL;
        }

        if (localName != NULL) {
                nodeptr->localName = strdup(localName);
                if (nodeptr->localName == NULL) {
                        return IXML_INSUFFICIENT_MEMORY;
                }
        }

        return IXML_SUCCESS;
}

const DOMString ixmlNode_getNamespaceURI(IXML_Node *nodeptr)
{
        DOMString retNamespaceURI = NULL;

        if (nodeptr != NULL) {
                retNamespaceURI = nodeptr->namespaceURI;
        }

        return retNamespaceURI;
}

const DOMString ixmlNode_getPrefix(IXML_Node *nodeptr)
{
        const DOMString prefix = NULL;

        if (nodeptr != NULL) {
                prefix = nodeptr->prefix;
        }

        return prefix;
}

const DOMString ixmlNode_getNodeValue(IXML_Node *nodeptr)
{
        if (nodeptr != NULL) {
                return nodeptr->nodeValue;
        }

        return NULL;
}

int ixmlNode_setNodeValue(IXML_Node *nodeptr, const char *newNodeValue)
{
        int rc = IXML_SUCCESS;

        if (nodeptr == NULL) {
                return IXML_INVALID_PARAMETER;
        }

        if (nodeptr->nodeValue != NULL) {
                free(nodeptr->nodeValue);
                nodeptr->nodeValue = NULL;
        }

        if (newNodeValue != NULL) {
                nodeptr->nodeValue = strdup(newNodeValue);
                if (nodeptr->nodeValue == NULL) {
                        return IXML_INSUFFICIENT_MEMORY;
                }
        }

        return rc;
}

unsigned short ixmlNode_getNodeType(IXML_Node *nodeptr)
{
        if (nodeptr != NULL) {
                return nodeptr->nodeType;
        } else {
                return (unsigned short)eINVALID_NODE;
        }
}

IXML_Node *ixmlNode_getParentNode(IXML_Node *nodeptr)
{
        if (nodeptr != NULL) {
                return nodeptr->parentNode;
        } else {
                return NULL;
        }
}

IXML_Node *ixmlNode_getFirstChild(IXML_Node *nodeptr)
{
        if (nodeptr != NULL) {
                return nodeptr->firstChild;
        } else {
                return NULL;
        }
}

IXML_Node *ixmlNode_getLastChild(IXML_Node *nodeptr)
{
        IXML_Node *prev;
        IXML_Node *next;

        if (nodeptr != NULL) {
                prev = nodeptr;
                next = nodeptr->firstChild;
                while (next != NULL) {
                        prev = next;
                        next = next->nextSibling;
                }
                return prev;
        } else {
                return NULL;
        }
}

IXML_Node *ixmlNode_getPreviousSibling(IXML_Node *nodeptr)
{
        if (nodeptr != NULL) {
                return nodeptr->prevSibling;
        } else {
                return NULL;
        }
}

IXML_Node *ixmlNode_getNextSibling(IXML_Node *nodeptr)
{
        if (nodeptr != NULL) {
                return nodeptr->nextSibling;
        } else {
                return NULL;
        }
}

IXML_Document *ixmlNode_getOwnerDocument(IXML_Node *nodeptr)
{
        if (nodeptr != NULL) {
                return (IXML_Document *)nodeptr->ownerDocument;
        } else {
                return NULL;
        }
}

/*!
 * \brief Check if ancestorNode is ancestor of toFind.
 *
 * \return 1 or 0.
 */
static int ixmlNode_isAncestor(
        /*! [in] The candidate to ancestor \b Node. */
        IXML_Node *ancestorNode,
        /*! [in] The \b Node to check for an ancestor. */
        IXML_Node *toFind)
{
        int found = 0;

        if (ancestorNode != NULL && toFind != NULL) {
                if (toFind->parentNode == ancestorNode) {
                        return 1;
                } else {
                        found = ixmlNode_isAncestor(
                                ancestorNode->firstChild, toFind);
                        if (found == 0) {
                                found = ixmlNode_isAncestor(
                                        ancestorNode->nextSibling, toFind);
                        }
                }
        }

        return found;
}

/*!
 * \brief Check whether toFind is a children of nodeptr.
 *
 * \return 1 or 0.
 */
static int ixmlNode_isParent(
        /*! [in] The candidate to parent \b Node. */
        IXML_Node *nodeptr,
        /*! [in] The \b Node to check for his parent. */
        IXML_Node *toFind)
{
        int found = 0;

        assert(nodeptr != NULL && toFind != NULL);

        if (nodeptr != NULL && toFind != NULL)
                found = toFind->parentNode == nodeptr;

        return found;
}

/*!
 * \brief Check to see whether nodeptr allows children of type newChild.
 *
 * \return
 * 	\li 1, if nodeptr can have newChild as children.
 * 	\li 0, if nodeptr cannot have newChild as children.
 */
static int ixmlNode_allowChildren(
        /*! [in] The \b Node to check. */
        IXML_Node *nodeptr,
        /*! [in] The child \b Node to check. */
        IXML_Node *newChild)
{
        assert(nodeptr != NULL && newChild != NULL);

        switch (nodeptr->nodeType) {
        case eATTRIBUTE_NODE:
        case eTEXT_NODE:
        case eCDATA_SECTION_NODE:
                return 0;

        case eELEMENT_NODE:
                switch (newChild->nodeType) {
                case eATTRIBUTE_NODE:
                case eDOCUMENT_NODE:
                        return 0;
                default:
                        break;
                }
                break;

        case eDOCUMENT_NODE:
                switch (newChild->nodeType) {
                case eELEMENT_NODE:
                        break;
                default:
                        return 0;
                }

        default:
                break;
        }

        return 1;
}

/*!
 * \brief Compare two nodes to see whether they are the same node.
 * Parent, sibling and children node are ignored.
 *
 * \return
 * 	\li 1, the two nodes are the same.
 * 	\li 0, the two nodes are not the same.
 */
int ixmlNode_compare(
        /*! [in] The first \b Node. */
        IXML_Node *srcNode,
        /*! [in] The second \b Node. */
        IXML_Node *destNode)
{
        assert(srcNode != NULL && destNode != NULL);

        return srcNode == destNode ||
                (strcmp(srcNode->nodeName, destNode->nodeName) == 0 &&
                        strcmp(srcNode->nodeValue, destNode->nodeValue) == 0 &&
                        srcNode->nodeType == destNode->nodeType &&
                        strcmp(srcNode->namespaceURI, destNode->namespaceURI) ==
                                0 &&
                        strcmp(srcNode->prefix, destNode->prefix) == 0 &&
                        strcmp(srcNode->localName, destNode->localName) == 0);
}

int ixmlNode_insertBefore(
        IXML_Node *nodeptr, IXML_Node *newChild, IXML_Node *refChild)
{
        int ret = IXML_SUCCESS;

        if (nodeptr == NULL || newChild == NULL) {
                return IXML_INVALID_PARAMETER;
        }
        /* whether nodeptr allow children of the type of newChild */
        if (ixmlNode_allowChildren(nodeptr, newChild) == 0) {
                return IXML_HIERARCHY_REQUEST_ERR;
        }
        /* or if newChild is one of nodeptr's ancestors */
        if (ixmlNode_isAncestor(newChild, nodeptr)) {
                return IXML_HIERARCHY_REQUEST_ERR;
        }
        /* if newChild was created from a different document */
        if (nodeptr->ownerDocument != newChild->ownerDocument) {
                return IXML_WRONG_DOCUMENT_ERR;
        }
        /* if refChild is not a child of nodeptr */
        if (ixmlNode_isParent(nodeptr, refChild) == 0) {
                return IXML_NOT_FOUND_ERR;
        }

        if (refChild != NULL) {
                if (ixmlNode_isParent(nodeptr, newChild)) {
                        ixmlNode_removeChild(nodeptr, newChild, &newChild);
                        newChild->nextSibling = NULL;
                        newChild->prevSibling = NULL;
                }
                newChild->nextSibling = refChild;
                if (refChild->prevSibling != NULL) {
                        refChild->prevSibling->nextSibling = newChild;
                        newChild->prevSibling = refChild->prevSibling;
                }
                refChild->prevSibling = newChild;
                if (newChild->prevSibling == NULL) {
                        nodeptr->firstChild = newChild;
                }
                newChild->parentNode = nodeptr;
        } else {
                ret = ixmlNode_appendChild(nodeptr, newChild);
        }

        return ret;
}

int ixmlNode_replaceChild(IXML_Node *nodeptr,
        IXML_Node *newChild,
        IXML_Node *oldChild,
        IXML_Node **returnNode)
{
        int ret = IXML_SUCCESS;

        if (nodeptr == NULL || newChild == NULL || oldChild == NULL) {
                return IXML_INVALID_PARAMETER;
        }
        /* if nodetype of nodeptr does not allow children of the type of
         * newChild needs to add later or if newChild is one of nodeptr's
         * ancestors */
        if (ixmlNode_isAncestor(newChild, nodeptr)) {
                return IXML_HIERARCHY_REQUEST_ERR;
        }

        if (ixmlNode_allowChildren(nodeptr, newChild) == 0) {
                return IXML_HIERARCHY_REQUEST_ERR;
        }
        /* if newChild was created from a different document */
        if (nodeptr->ownerDocument != newChild->ownerDocument) {
                return IXML_WRONG_DOCUMENT_ERR;
        }
        /* if refChild is not a child of nodeptr */
        if (ixmlNode_isParent(nodeptr, oldChild) != 1) {
                return IXML_NOT_FOUND_ERR;
        }

        ret = ixmlNode_insertBefore(nodeptr, newChild, oldChild);
        if (ret != IXML_SUCCESS) {
                return ret;
        }

        ret = ixmlNode_removeChild(nodeptr, oldChild, returnNode);
        return ret;
}

int ixmlNode_removeChild(
        IXML_Node *nodeptr, IXML_Node *oldChild, IXML_Node **returnNode)
{
        if (!nodeptr || !oldChild)
                return IXML_INVALID_PARAMETER;
        if (!ixmlNode_isParent(nodeptr, oldChild))
                return IXML_NOT_FOUND_ERR;
        if (oldChild->prevSibling)
                oldChild->prevSibling->nextSibling = oldChild->nextSibling;
        if (nodeptr->firstChild == oldChild)
                nodeptr->firstChild = oldChild->nextSibling;
        if (oldChild->nextSibling)
                oldChild->nextSibling->prevSibling = oldChild->prevSibling;
        oldChild->nextSibling = NULL;
        oldChild->prevSibling = NULL;
        oldChild->parentNode = NULL;
        if (returnNode)
                *returnNode = oldChild;
        else
                ixmlNode_free(oldChild);

        return IXML_SUCCESS;
}

int ixmlNode_appendChild(IXML_Node *nodeptr, IXML_Node *newChild)
{
        IXML_Node *prev = NULL;
        IXML_Node *next = NULL;

        if (nodeptr == NULL || newChild == NULL) {
                return IXML_INVALID_PARAMETER;
        }
        /* if newChild was created from a different document */
        if (newChild->ownerDocument != NULL &&
                nodeptr->ownerDocument != newChild->ownerDocument) {
                return IXML_WRONG_DOCUMENT_ERR;
        }
        /* if newChild is an ancestor of nodeptr */
        if (ixmlNode_isAncestor(newChild, nodeptr)) {
                return IXML_HIERARCHY_REQUEST_ERR;
        }
        /* if nodeptr does not allow to have newChild as children */
        if (ixmlNode_allowChildren(nodeptr, newChild) == 0) {
                return IXML_HIERARCHY_REQUEST_ERR;
        }

        if (ixmlNode_isParent(nodeptr, newChild)) {
                ixmlNode_removeChild(nodeptr, newChild, &newChild);
        }
        /* set the parent node pointer */
        newChild->parentNode = nodeptr;
        newChild->ownerDocument = nodeptr->ownerDocument;

        /* if the first child */
        if (nodeptr->firstChild == NULL) {
                nodeptr->firstChild = newChild;
        } else {
                prev = nodeptr->firstChild;
                next = prev->nextSibling;
                while (next != NULL) {
                        prev = next;
                        next = prev->nextSibling;
                }
                prev->nextSibling = newChild;
                newChild->prevSibling = prev;
        }

        return IXML_SUCCESS;
}

/*!
 * \brief Returns a clone of nodeptr.
 *
 * \return A cloned node of nodeptr.
 */
static IXML_Node *ixmlNode_cloneTextNode(
        /*! [in] The \b Node to clone. */
        IXML_Node *nodeptr)
{
        IXML_Node *newNode = NULL;
        int rc;

        assert(nodeptr != NULL);

        newNode = (IXML_Node *)malloc(sizeof(IXML_Node));
        if (newNode == NULL) {
                return NULL;
        } else {
                ixmlNode_init(newNode);
                rc = ixmlNode_setNodeName(newNode, nodeptr->nodeName);
                if (rc != IXML_SUCCESS) {
                        ixmlNode_free(newNode);
                        return NULL;
                }
                rc = ixmlNode_setNodeValue(newNode, nodeptr->nodeValue);
                if (rc != IXML_SUCCESS) {
                        ixmlNode_free(newNode);
                        return NULL;
                }
                newNode->nodeType = eTEXT_NODE;
        }

        return newNode;
}

/*!
 * \brief Return a clone of CDATASection node.
 *
 * \return A clone of CDATASection node.
 */
static IXML_CDATASection *ixmlNode_cloneCDATASect(
        /*! [in] The \b Node to clone. */
        IXML_CDATASection *nodeptr)
{
        IXML_CDATASection *newCDATA = NULL;
        IXML_Node *newNode;
        IXML_Node *srcNode;
        int rc;

        assert(nodeptr != NULL);
        newCDATA = (IXML_CDATASection *)malloc(sizeof(IXML_CDATASection));
        if (newCDATA != NULL) {
                newNode = (IXML_Node *)newCDATA;
                ixmlCDATASection_init(newCDATA);
                srcNode = (IXML_Node *)nodeptr;
                rc = ixmlNode_setNodeName(newNode, srcNode->nodeName);
                if (rc != IXML_SUCCESS) {
                        ixmlCDATASection_free(newCDATA);
                        return NULL;
                }
                rc = ixmlNode_setNodeValue(newNode, srcNode->nodeValue);
                if (rc != IXML_SUCCESS) {
                        ixmlCDATASection_free(newCDATA);
                        return NULL;
                }
                newNode->nodeType = eCDATA_SECTION_NODE;
        }

        return newCDATA;
}

/*!
 * \brief Returns a clone of element node.
 *
 * \return A clone of element node.
 */
static IXML_Element *ixmlNode_cloneElement(
        /*! [in] The \b Node to clone. */
        IXML_Element *nodeptr)
{
        IXML_Element *newElement;
        IXML_Node *elementNode;
        IXML_Node *srcNode;
        int rc;

        assert(nodeptr != NULL);

        newElement = (IXML_Element *)malloc(sizeof(IXML_Element));
        if (newElement == NULL) {
                return NULL;
        }

        ixmlElement_init(newElement);
        rc = ixmlElement_setTagName(newElement, nodeptr->tagName);
        if (rc != IXML_SUCCESS) {
                ixmlElement_free(newElement);
                return NULL;
        }

        elementNode = (IXML_Node *)newElement;
        srcNode = (IXML_Node *)nodeptr;
        rc = ixmlNode_setNodeName(elementNode, srcNode->nodeName);
        if (rc != IXML_SUCCESS) {
                ixmlElement_free(newElement);
                return NULL;
        }

        rc = ixmlNode_setNodeValue(elementNode, srcNode->nodeValue);
        if (rc != IXML_SUCCESS) {
                ixmlElement_free(newElement);
                return NULL;
        }

        rc = ixmlNode_setNamespaceURI(elementNode, srcNode->namespaceURI);
        if (rc != IXML_SUCCESS) {
                ixmlElement_free(newElement);
                return NULL;
        }

        rc = ixmlNode_setPrefix(elementNode, srcNode->prefix);
        if (rc != IXML_SUCCESS) {
                ixmlElement_free(newElement);
                return NULL;
        }

        rc = ixmlNode_setLocalName(elementNode, srcNode->localName);
        if (rc != IXML_SUCCESS) {
                ixmlElement_free(newElement);
                return NULL;
        }

        elementNode->nodeType = eELEMENT_NODE;

        return newElement;
}

/*!
 * \brief Returns a new document node.
 *
 * Currently, the IXML_Document struct is just a node, so this function
 * just mallocs the IXML_Document, sets the node type and name.
 *
 * \return A new document node.
 */
static IXML_Document *ixmlNode_newDoc(void)
{
        IXML_Document *newDoc;
        IXML_Node *docNode;
        int rc;

        newDoc = (IXML_Document *)malloc(sizeof(IXML_Document));
        if (!newDoc)
                return NULL;
        ixmlDocument_init(newDoc);
        docNode = (IXML_Node *)newDoc;
        rc = ixmlNode_setNodeName(docNode, DOCUMENTNODENAME);
        if (rc != IXML_SUCCESS) {
                ixmlDocument_free(newDoc);
                return NULL;
        }
        newDoc->n.nodeType = eDOCUMENT_NODE;

        return newDoc;
}

/*!
 * \brief Returns a clone of an attribute node.
 *
 * \return A clone of an attribute node.
 */
static IXML_Attr *ixmlNode_cloneAttr(
        /*! [in] The \b Node to clone. */
        IXML_Attr *nodeptr)
{
        IXML_Attr *newAttr;
        IXML_Node *attrNode;
        IXML_Node *srcNode;
        int rc;

        assert(nodeptr != NULL);

        newAttr = (IXML_Attr *)malloc(sizeof(IXML_Attr));
        if (newAttr == NULL) {
                return NULL;
        }

        ixmlAttr_init(newAttr);
        attrNode = (IXML_Node *)newAttr;
        srcNode = (IXML_Node *)nodeptr;

        rc = ixmlNode_setNodeName(attrNode, srcNode->nodeName);
        if (rc != IXML_SUCCESS) {
                ixmlAttr_free(newAttr);
                return NULL;
        }

        rc = ixmlNode_setNodeValue(attrNode, srcNode->nodeValue);
        if (rc != IXML_SUCCESS) {
                ixmlAttr_free(newAttr);
                return NULL;
        }

        /* Check to see whether we need to split prefix and localname for
         * attribute */
        rc = ixmlNode_setNamespaceURI(attrNode, srcNode->namespaceURI);
        if (rc != IXML_SUCCESS) {
                ixmlAttr_free(newAttr);
                return NULL;
        }

        rc = ixmlNode_setPrefix(attrNode, srcNode->prefix);
        if (rc != IXML_SUCCESS) {
                ixmlAttr_free(newAttr);
                return NULL;
        }

        rc = ixmlNode_setLocalName(attrNode, srcNode->localName);
        if (rc != IXML_SUCCESS) {
                ixmlAttr_free(newAttr);
                return NULL;
        }

        attrNode->nodeType = eATTRIBUTE_NODE;

        return newAttr;
}

/*!
 * \brief Return a clone of attribute node, with specified field set to 1.
 *
 * \return A clone of attribute node, with specified field set to 1.
 */
static IXML_Attr *ixmlNode_cloneAttrDirect(
        /*! [in] The \b Node to clone. */
        IXML_Attr *nodeptr)
{
        IXML_Attr *newAttr;

        assert(nodeptr != NULL);

        newAttr = ixmlNode_cloneAttr(nodeptr);
        if (newAttr != NULL) {
                newAttr->specified = 1;
        }

        return newAttr;
}

/*!
 * \brief Sets siblings nodes parent to be the same as this node's.
 */
static void ixmlNode_setSiblingNodesParent(
        /*! [in] The node to operate on. */
        IXML_Node *nodeptr)
{
        IXML_Node *parentNode = nodeptr->parentNode;
        IXML_Node *nextptr = nodeptr->nextSibling;

        while (nextptr != NULL) {
                nextptr->parentNode = parentNode;
                nextptr = nextptr->nextSibling;
        }
}

/*!
 * \brief Recursive function that clones a node tree of nodeptr.
 *
 * \returns The cloned node/tree.
 */
static IXML_Node *ixmlNode_cloneNodeTreeRecursive(
        /*! [in] Node tree to clone. */
        IXML_Node *nodeptr,
        /*! [in] 1 if you want to clone the tree. */
        int deep)
{
        IXML_Node *newNode = NULL;
        IXML_Element *newElement = NULL;
        IXML_Attr *newAttr = NULL;
        IXML_CDATASection *newCDATA = NULL;
        IXML_Document *newDoc = NULL;
        IXML_Node *nextSib = NULL;

        if (nodeptr != NULL) {
                switch (nodeptr->nodeType) {
                case eELEMENT_NODE:
                        newElement =
                                ixmlNode_cloneElement((IXML_Element *)nodeptr);
                        if (newElement == NULL)
                                return NULL;
                        newElement->n.firstAttr =
                                ixmlNode_cloneNodeTreeRecursive(
                                        nodeptr->firstAttr, deep);
                        if (deep) {
                                newElement->n.firstChild =
                                        ixmlNode_cloneNodeTreeRecursive(
                                                nodeptr->firstChild, deep);
                                if (newElement->n.firstChild != NULL) {
                                        newElement->n.firstChild->parentNode =
                                                (IXML_Node *)newElement;
                                        ixmlNode_setSiblingNodesParent(
                                                newElement->n.firstChild);
                                }
                                nextSib = ixmlNode_cloneNodeTreeRecursive(
                                        nodeptr->nextSibling, deep);
                                newElement->n.nextSibling = nextSib;
                                if (nextSib != NULL) {
                                        nextSib->prevSibling =
                                                (IXML_Node *)newElement;
                                }
                        }
                        newNode = (IXML_Node *)newElement;
                        break;

                case eATTRIBUTE_NODE:
                        newAttr = ixmlNode_cloneAttr((IXML_Attr *)nodeptr);
                        if (newAttr == NULL)
                                return NULL;
                        nextSib = ixmlNode_cloneNodeTreeRecursive(
                                nodeptr->nextSibling, deep);
                        newAttr->n.nextSibling = nextSib;
                        if (nextSib != NULL) {
                                nextSib->prevSibling = (IXML_Node *)newAttr;
                        }
                        newNode = (IXML_Node *)newAttr;
                        break;

                case eTEXT_NODE:
                        newNode = ixmlNode_cloneTextNode(nodeptr);
                        break;

                case eCDATA_SECTION_NODE:
                        newCDATA = ixmlNode_cloneCDATASect(
                                (IXML_CDATASection *)nodeptr);
                        newNode = (IXML_Node *)newCDATA;
                        break;

                case eDOCUMENT_NODE:
                        newDoc = ixmlNode_newDoc();
                        if (newDoc == NULL)
                                return NULL;
                        newNode = (IXML_Node *)newDoc;
                        if (deep) {
                                newNode->firstChild =
                                        ixmlNode_cloneNodeTreeRecursive(
                                                nodeptr->firstChild, deep);
                                if (newNode->firstChild != NULL) {
                                        newNode->firstChild->parentNode =
                                                newNode;
                                }
                        }
                        break;

                case eINVALID_NODE:
                case eENTITY_REFERENCE_NODE:
                case eENTITY_NODE:
                case ePROCESSING_INSTRUCTION_NODE:
                case eCOMMENT_NODE:
                case eDOCUMENT_TYPE_NODE:
                case eDOCUMENT_FRAGMENT_NODE:
                case eNOTATION_NODE:
                        break;
                }
        }

        return newNode;
}

/*!
 * \brief Function that clones a node tree of nodeptr.
 *
 * \returns The cloned node/tree.
 */
static IXML_Node *ixmlNode_cloneNodeTree(
        /*! [in] Node tree to clone. */
        IXML_Node *nodeptr,
        /*! [in] 1 if you want to clone the tree. */
        int deep)
{
        IXML_Node *newNode = NULL;
        IXML_Element *newElement;
        IXML_Node *childNode;

        assert(nodeptr != NULL);

        switch (nodeptr->nodeType) {
        case eELEMENT_NODE:
                newElement = ixmlNode_cloneElement((IXML_Element *)nodeptr);
                if (newElement == NULL)
                        return NULL;
                newElement->n.firstAttr = ixmlNode_cloneNodeTreeRecursive(
                        nodeptr->firstAttr, deep);
                if (deep) {
                        newElement->n.firstChild =
                                ixmlNode_cloneNodeTreeRecursive(
                                        nodeptr->firstChild, deep);
                        childNode = newElement->n.firstChild;
                        while (childNode != NULL) {
                                childNode->parentNode = (IXML_Node *)newElement;
                                childNode = childNode->nextSibling;
                        }
                        newElement->n.nextSibling = NULL;
                }
                newNode = (IXML_Node *)newElement;
                break;

        case eATTRIBUTE_NODE:
        case eTEXT_NODE:
        case eCDATA_SECTION_NODE:
        case eDOCUMENT_NODE:
                newNode = ixmlNode_cloneNodeTreeRecursive(nodeptr, deep);
                break;

        case eINVALID_NODE:
        case eENTITY_REFERENCE_NODE:
        case eENTITY_NODE:
        case ePROCESSING_INSTRUCTION_NODE:
        case eCOMMENT_NODE:
        case eDOCUMENT_TYPE_NODE:
        case eDOCUMENT_FRAGMENT_NODE:
        case eNOTATION_NODE:
#if 0
		/* create a new node here? */
		newNode = (IXML_Node *)malloc(sizeof(IXML_Node));
		if (newNode == NULL) {
			return NULL;
		}
#endif
                break;
        }

        /* by spec, the duplicate node has no parent */
        if (newNode != NULL)
                newNode->parentNode = NULL;

        return newNode;
}

IXML_Node *ixmlNode_cloneNode(IXML_Node *nodeptr, int deep)
{
        IXML_Node *newNode;
        IXML_Attr *newAttrNode;

        if (nodeptr == NULL) {
                return NULL;
        }

        switch (nodeptr->nodeType) {
        case eATTRIBUTE_NODE:
                newAttrNode = ixmlNode_cloneAttrDirect((IXML_Attr *)nodeptr);
                return (IXML_Node *)newAttrNode;
                break;

        default:
                newNode = ixmlNode_cloneNodeTree(nodeptr, deep);
                return newNode;
                break;
        }
}

IXML_NodeList *ixmlNode_getChildNodes(IXML_Node *nodeptr)
{
        IXML_Node *tempNode;
        IXML_NodeList *newNodeList;
        int rc;

        if (nodeptr == NULL) {
                return NULL;
        }

        newNodeList = (IXML_NodeList *)malloc(sizeof(IXML_NodeList));
        if (newNodeList == NULL) {
                return NULL;
        }

        ixmlNodeList_init(newNodeList);
        tempNode = nodeptr->firstChild;
        while (tempNode != NULL) {
                rc = ixmlNodeList_addToNodeList(&newNodeList, tempNode);
                if (rc != IXML_SUCCESS) {
                        ixmlNodeList_free(newNodeList);
                        return NULL;
                }

                tempNode = tempNode->nextSibling;
        }

        return newNodeList;
}

IXML_NamedNodeMap *ixmlNode_getAttributes(IXML_Node *nodeptr)
{
        IXML_NamedNodeMap *returnNamedNodeMap = NULL;
        IXML_Node *tempNode;
        int rc;

        if (nodeptr == NULL) {
                return NULL;
        }

        switch (nodeptr->nodeType) {
        case eELEMENT_NODE:
                returnNamedNodeMap =
                        (IXML_NamedNodeMap *)malloc(sizeof(IXML_NamedNodeMap));
                if (returnNamedNodeMap == NULL) {
                        return NULL;
                }

                ixmlNamedNodeMap_init(returnNamedNodeMap);
                tempNode = nodeptr->firstAttr;
                while (tempNode != NULL) {
                        rc = ixmlNamedNodeMap_addToNamedNodeMap(
                                &returnNamedNodeMap, tempNode);
                        if (rc != IXML_SUCCESS) {
                                ixmlNamedNodeMap_free(returnNamedNodeMap);
                                return NULL;
                        }

                        tempNode = tempNode->nextSibling;
                }
                return returnNamedNodeMap;
        default:
                /* if not an ELEMENT_NODE */
                return NULL;
        }
}

int ixmlNode_hasChildNodes(IXML_Node *nodeptr)
{
        if (nodeptr == NULL) {
                return 0;
        }

        return nodeptr->firstChild != NULL;
}

int ixmlNode_hasAttributes(IXML_Node *nodeptr)
{
        if (nodeptr != NULL) {
                switch (nodeptr->nodeType) {
                case eELEMENT_NODE:
                        if (nodeptr->firstAttr != NULL)
                                return 1;
                        break;
                default:
                        break;
                }
        }

        return 0;
}

/*!
 * \brief Recursively traverse the whole tree, search for element with the
 * given tagname.
 */
static void ixmlNode_getElementsByTagNameRecursive(
        /*! [in] The \b Node tree. */
        IXML_Node *n,
        /*! [in] The tag name to match. */
        const char *tagname,
        /*! [out] The output \b NodeList. */
        IXML_NodeList **list)
{
        const char *name;

        if (n != NULL) {
                if (ixmlNode_getNodeType(n) == eELEMENT_NODE) {
                        name = ixmlNode_getNodeName(n);
                        if (strcmp(tagname, name) == 0 ||
                                strcmp(tagname, "*") == 0) {
                                ixmlNodeList_addToNodeList(list, n);
                        }
                }
                ixmlNode_getElementsByTagNameRecursive(
                        ixmlNode_getFirstChild(n), tagname, list);
                ixmlNode_getElementsByTagNameRecursive(
                        ixmlNode_getNextSibling(n), tagname, list);
        }
}

void ixmlNode_getElementsByTagName(
        IXML_Node *n, const char *tagname, IXML_NodeList **list)
{
        const char *name;

        assert(n != NULL && tagname != NULL);

        if (ixmlNode_getNodeType(n) == eELEMENT_NODE) {
                name = ixmlNode_getNodeName(n);
                if (strcmp(tagname, name) == 0 || strcmp(tagname, "*") == 0) {
                        ixmlNodeList_addToNodeList(list, n);
                }
        }
        ixmlNode_getElementsByTagNameRecursive(
                ixmlNode_getFirstChild(n), tagname, list);
}

/*!
 * \brief
 */
static void ixmlNode_getElementsByTagNameNSRecursive(
        /*! [in] . */
        IXML_Node *n,
        /*! [in] . */
        const char *namespaceURI,
        /*! [in] . */
        const char *localName,
        /*! [out] . */
        IXML_NodeList **list)
{
        const DOMString nsURI;
        const DOMString name;

        if (n != NULL) {
                if (ixmlNode_getNodeType(n) == eELEMENT_NODE) {
                        name = ixmlNode_getLocalName(n);
                        nsURI = ixmlNode_getNamespaceURI(n);

                        if (name != NULL && nsURI != NULL &&
                                (strcmp(namespaceURI, nsURI) == 0 ||
                                        strcmp(namespaceURI, "*") == 0) &&
                                (strcmp(name, localName) == 0 ||
                                        strcmp(localName, "*") == 0)) {
                                ixmlNodeList_addToNodeList(list, n);
                        }
                }
                ixmlNode_getElementsByTagNameNSRecursive(
                        ixmlNode_getFirstChild(n),
                        namespaceURI,
                        localName,
                        list);
                ixmlNode_getElementsByTagNameNSRecursive(
                        ixmlNode_getNextSibling(n),
                        namespaceURI,
                        localName,
                        list);
        }
}

void ixmlNode_getElementsByTagNameNS(IXML_Node *n,
        const char *namespaceURI,
        const char *localName,
        IXML_NodeList **list)
{
        const DOMString nsURI;
        const DOMString name;

        assert(n != NULL && namespaceURI != NULL && localName != NULL);

        if (ixmlNode_getNodeType(n) == eELEMENT_NODE) {
                name = ixmlNode_getLocalName(n);
                nsURI = ixmlNode_getNamespaceURI(n);
                if (name != NULL && nsURI != NULL &&
                        (strcmp(namespaceURI, nsURI) == 0 ||
                                strcmp(namespaceURI, "*") == 0) &&
                        (strcmp(name, localName) == 0 ||
                                strcmp(localName, "*") == 0)) {
                        ixmlNodeList_addToNodeList(list, n);
                }
        }

        ixmlNode_getElementsByTagNameNSRecursive(
                ixmlNode_getFirstChild(n), namespaceURI, localName, list);
}

int ixmlNode_setNodeName(IXML_Node *node, const DOMString qualifiedName)
{
        int rc = IXML_SUCCESS;

        assert(node != NULL);

        if (node->nodeName != NULL) {
                free(node->nodeName);
                node->nodeName = NULL;
        }

        if (qualifiedName != NULL) {
                /* set the name part */
                node->nodeName = strdup(qualifiedName);
                if (node->nodeName == NULL) {
                        return IXML_INSUFFICIENT_MEMORY;
                }

                rc = Parser_setNodePrefixAndLocalName(node);
                if (rc != IXML_SUCCESS) {
                        free(node->nodeName);
                }
        }

        return rc;
}

int ixmlNode_setNodeProperties(IXML_Node *destNode, IXML_Node *src)
{
        int rc;

        assert(destNode != NULL && src != NULL);
        if (destNode == NULL || src == NULL) {
                return IXML_INVALID_PARAMETER;
        }

        rc = ixmlNode_setNodeValue(destNode, src->nodeValue);
        if (rc != IXML_SUCCESS) {
                goto ErrorHandler;
        }

        rc = ixmlNode_setLocalName(destNode, src->localName);
        if (rc != IXML_SUCCESS) {
                goto ErrorHandler;
        }

        rc = ixmlNode_setPrefix(destNode, src->prefix);
        if (rc != IXML_SUCCESS) {
                goto ErrorHandler;
        }
        /* set nodetype */
        destNode->nodeType = src->nodeType;

        return IXML_SUCCESS;

ErrorHandler:
        if (destNode->nodeName != NULL) {
                free(destNode->nodeName);
                destNode->nodeName = NULL;
        }
        if (destNode->nodeValue != NULL) {
                free(destNode->nodeValue);
                destNode->nodeValue = NULL;
        }
        if (destNode->localName != NULL) {
                free(destNode->localName);
                destNode->localName = NULL;
        }

        return IXML_INSUFFICIENT_MEMORY;
}

#ifdef IXML_HAVE_SCRIPTSUPPORT
void ixmlNode_setCTag(IXML_Node *nodeptr, void *ctag)
{
        if (nodeptr != NULL)
                nodeptr->ctag = ctag;
}

void *ixmlNode_getCTag(IXML_Node *nodeptr)
{
        if (nodeptr != NULL)
                return nodeptr->ctag;
        else
                return NULL;
}
#endif
